﻿using AntC.AspNetCore.ApiEncryption.EncryptProviders;
using AntC.AspNetCore.ApiEncryption.Internal.Interfaces;
using AntC.AspNetCore.ApiEncryption.PolicyProviders;
using Microsoft.Extensions.DependencyInjection;
using System;

namespace AntC.AspNetCore.ApiEncryption
{
    /// <summary>
    /// api encryption extension methods for services
    /// </summary>
    public static class ApiEncryptionServicesExtensions
    {
        /// <summary>
        /// Add api encryption services.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
        /// <returns></returns>
        public static IServiceCollection AddApiEncryption(this IServiceCollection services)
        {
            return services.AddApiEncryption(typeof(Base64EncryptProvider), typeof(UrlRegexEncryptionPolicyProvider));
        }

        /// <summary>
        /// Add api encryption services.
        /// </summary>
        /// <typeparam name="T">The type is implemented <see cref="IEncryptProvider"/> interface.</typeparam>
        /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
        /// <returns></returns>
        public static IServiceCollection AddApiEncryption<T>(this IServiceCollection services)
            where T : IEncryptProvider
        {
            return services.AddApiEncryption(typeof(T), typeof(UrlRegexEncryptionPolicyProvider));
        }

        /// <summary>
        /// Add api encryption services.
        /// </summary>
        /// <typeparam name="T">The type is implemented <see cref="IEncryptProvider"/> interface.</typeparam>
        /// <typeparam name="K">The type is implemented <see cref="IApiEncryptionPolicyProvider"/> interface.</typeparam>
        /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
        /// <returns></returns>
        public static IServiceCollection AddApiEncryption<T, K>(this IServiceCollection services)
            where T : IEncryptProvider
            where K : IApiEncryptionPolicyProvider
        {
            return services.AddApiEncryption(typeof(T), typeof(K));
        }

        /// <summary>
        /// Add api encryption services.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
        /// <param name="encryptProviderType"></param>
        /// <returns></returns>
        public static IServiceCollection AddApiEncryption(this IServiceCollection services, Type encryptProviderType)
        {
            return services.AddApiEncryption(encryptProviderType, typeof(UrlRegexEncryptionPolicyProvider));
        }

        /// <summary>
        /// Add api encryption services.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
        /// <param name="encryptProviderType">The encrypt provider type</param>
        /// <param name="policyProviderType">The policy provider type</param>
        /// <returns></returns>
        public static IServiceCollection AddApiEncryption(this IServiceCollection services, Type encryptProviderType, Type policyProviderType)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            if (encryptProviderType == null)
            {
                throw new ArgumentNullException(nameof(encryptProviderType));
            }

            if (policyProviderType == null)
            {
                throw new ArgumentNullException(nameof(policyProviderType));
            }

            if (!typeof(IEncryptProvider).IsAssignableFrom(encryptProviderType))
            {
                throw new ArgumentException($"type \"{nameof(encryptProviderType)}\" is not implementation interface \"{nameof(IEncryptProvider)}\".");
            }

            if (!typeof(IApiEncryptionPolicyProvider).IsAssignableFrom(policyProviderType))
            {
                throw new ArgumentException($"type \"{nameof(policyProviderType)}\" is not implementation interface \"{nameof(IApiEncryptionPolicyProvider)}\".");
            }

            //services.TryAdd(ServiceDescriptor.Singleton(typeof(IEncryptProvider), encryptProviderType));
            //services.TryAdd(ServiceDescriptor.Singleton(typeof(IApiEncryptionPolicyProvider), policyProviderType));
            services.AddScoped(typeof(IEncryptProvider), encryptProviderType);
            services.AddScoped(typeof(IApiEncryptionPolicyProvider), policyProviderType);
            return services;
        }

        /// <summary>
        /// Add api encryption services.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection"/> for adding services.</param>
        /// <param name="encryptProviderType">The encrypt provider type</param>
        /// <param name="policyProviderType">The policy provider type</param>
        /// <param name="configureOptions">A delegate to configure the <see cref="ApiEncryptionOptions"/>.</param>
        /// <returns></returns>
        public static IServiceCollection AddApiEncryption(this IServiceCollection services, Action<ApiEncryptionOptions> configureOptions, Type encryptProviderType = null, Type policyProviderType = null)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }
            if (configureOptions == null)
            {
                throw new ArgumentNullException(nameof(configureOptions));
            }
            if (encryptProviderType == null)
            {
                encryptProviderType = typeof(Base64EncryptProvider);
            }
            if (policyProviderType == null)
            {
                policyProviderType = typeof(UrlRegexEncryptionPolicyProvider);
            }

            services.Configure(configureOptions);
            services.AddApiEncryption(encryptProviderType, policyProviderType);

            return services;
        }
    }
}
